package com.cxyd.modules.wms.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cxyd.common.constant.Constant;
import com.cxyd.common.constant.enums.wms.*;
import com.cxyd.common.exception.RootException;
import com.cxyd.common.page.PageData;
import com.cxyd.common.service.impl.CrudServiceImpl;
import com.cxyd.common.utils.Result;
import com.cxyd.modules.api.dao.RmsNoticeDao;
import com.cxyd.modules.api.dto.request.PurchaseDetailRequest;
import com.cxyd.modules.api.entity.RmsNoticeEntity;
import com.cxyd.modules.sys.dto.SysUserDTO;
import com.cxyd.modules.sys.service.SysUserService;
import com.cxyd.modules.wms.dao.*;
import com.cxyd.modules.wms.dto.PurchaseDTO;
import com.cxyd.modules.wms.dto.PurchaseDetailDTO;
import com.cxyd.modules.wms.entity.*;
import com.cxyd.modules.wms.service.*;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 入库明细
 *
 * @author g_Cycle gengruyi01@163.com
 * @since 1.0.0 2023-04-27
 */
@Service
public class PurchaseDetailServiceImpl extends CrudServiceImpl<PurchaseDetailDao, PurchaseDetailEntity, PurchaseDetailDTO> implements PurchaseDetailService {
    private static final Logger logger = LoggerFactory.getLogger(PurchaseDetailServiceImpl.class);

    private final SysUserService userService;
    private final PurchaseService purchaseService;
    private final PurchaseDetailDao purchaseDetailDao;
    private final SpuDao spuDao;
    private final ContainerDao containerDao;
    private final StockDao stockDao;
    private final LogWorkService logWorkService;
    private final TaskService taskService;
    private final TaskDao taskDao;
    private final LocateDao locateDao;
    private final PostWcsService postWcsService;
    private final StationDao stationDao;
    private final PurchaseDao purchaseDao;
    private final CommonService commonService;
    private final LocateService locateService;
    private final RmsNoticeDao rmsNoticeDao;

    public PurchaseDetailServiceImpl(SysUserService userService, PurchaseService purchaseService, PurchaseDetailDao purchaseDetailDao, SpuDao spuDao, ContainerDao containerDao, StockDao stockDao, LogWorkService logWorkService, TaskService taskService, TaskDao taskDao, LocateDao locateDao, PostWcsService postWcsService, StationDao stationDao, PurchaseDao purchaseDao, CommonService commonService, LocateService locateService, RmsNoticeDao rmsNoticeDao) {
        this.userService = userService;
        this.purchaseService = purchaseService;
        this.purchaseDetailDao = purchaseDetailDao;
        this.spuDao = spuDao;
        this.containerDao = containerDao;
        this.stockDao = stockDao;
        this.logWorkService = logWorkService;
        this.taskService = taskService;
        this.taskDao = taskDao;
        this.locateDao = locateDao;
        this.postWcsService = postWcsService;
        this.stationDao = stationDao;
        this.purchaseDao = purchaseDao;
        this.commonService = commonService;
        this.locateService = locateService;
        this.rmsNoticeDao = rmsNoticeDao;
    }

    @Override
    public PageData<PurchaseDetailDTO> page(Map<String, Object> params) {
        //1.查询主单信息
        final IPage<PurchaseDetailEntity> page = baseDao.selectPage(
                getPage(params, "purchase_id", true),
                getWrapper(params)
        );
        //2.获取用户信息
        final List<SysUserDTO> userList = userService.getList();
        //3.将creator与updater的username通过id进行绑定
        page.getRecords().forEach(entity -> {
            if (entity.getCreator() != null) {
                entity.setCreatorName(userService.getUsernameById(entity.getCreator(), userList));
            }
            if (entity.getUpdater() != null) {
                entity.setUpdaterName(userService.getUsernameById(entity.getUpdater(), userList));
            }
        });
        PageData<PurchaseDetailDTO> pageData = getPageData(page, PurchaseDetailDTO.class);
        //4.获取spu信息
        if (null != pageData) {
            List<PurchaseDetailDTO> records = pageData.getList();
            if (!CollectionUtils.isEmpty(records)) {
                List<String> spuNoList = records.stream().map(PurchaseDetailDTO::getSpuNo).collect(Collectors.toList());
                List<SpuEntity> spuEntities = spuDao.selectList(new QueryWrapper<SpuEntity>().in("spu_no", spuNoList));
                Map<String, SpuEntity> spuMap = spuEntities.stream().collect(Collectors.toMap(SpuEntity::getSpuNo, SpuEntity -> SpuEntity));
                records.forEach(entity -> {
                    SpuEntity spuEntity = spuMap.get(entity.getSpuNo());
                    if (null != spuEntity) {
                        entity.setSpuNoOld(spuEntity.getSpuNoOld());
                        entity.setEquipNo(spuEntity.getEquipNo());
                        entity.setEquipName(spuEntity.getEquipName());
                        entity.setDrawNo(spuEntity.getDrawNo());
                        entity.setSpuSpec(spuEntity.getSpuSpec());
                        entity.setSpuUnit(spuEntity.getSpuUnit());
                        entity.setSpuLevel(spuEntity.getSpuLevel());
                        entity.setEquipType(spuEntity.getEquipType());
                    }
                });
            }
        }
        return pageData;
    }

    @Override
    public PageData<PurchaseDetailDTO> rfPage(Map<String, Object> params) {
        final PurchaseDTO dto = purchaseService.selectByParams(params);
        if (dto == null) {
            return null;
        }
        final QueryWrapper<PurchaseDetailEntity> rfWrapper = new QueryWrapper<>();
        rfWrapper.eq("purchase_id", dto.getPurchaseId());
        rfWrapper.lt("detail_st", 2);
        rfWrapper.orderBy(true, false, "spu_name");
        final IPage<PurchaseDetailEntity> page = baseDao.selectPage(
                getPage(params, "detail_id", true),
                rfWrapper
        );
        return getPageData(page, PurchaseDetailDTO.class);
    }

    @Override
    public void batchSave(List<PurchaseDetailEntity> entities) {
        if (entities == null) {
            return;
        }
        baseDao.batchSave(entities);
    }

    @Override
    public List<PurchaseDetailEntity> detailList(String deliverNo) {
        PurchaseEntity purchaseEntity = purchaseDao.selectOne(new QueryWrapper<PurchaseEntity>().eq("deliver_no", deliverNo));
        if (ObjectUtils.isEmpty(purchaseEntity)) {
            throw new RootException("收货单[" + deliverNo + "]在系统中不存在!");
        }
        if (PurchaseStatusEnum.COMPLETE.getCode() == purchaseEntity.getPurchaseSt()) {
            throw new RootException("收货单[" + deliverNo + "]已完成收货！");
        }
        List<PurchaseDetailEntity> purchaseDetailList = purchaseDetailDao.selectList(new QueryWrapper<PurchaseDetailEntity>()
                .eq("purchase_id", purchaseEntity.getPurchaseId())
                .ne("detail_st", PurchaseStatusEnum.COMPLETE.getCode()));
        return purchaseDetailList;
    }

    @Override
    public List<PurchaseDetailEntity> findByPurchaseId(Long purchaseId) {
        final QueryWrapper<PurchaseDetailEntity> wrapper = new QueryWrapper<>();
        wrapper.eq(purchaseId != null, "purchase_id", purchaseId);
        return baseDao.selectList(wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result purchaseRegister(PurchaseDetailRequest request) {
        String contNo = request.getContNo();
        BigDecimal num = new BigDecimal(request.getNum().toString());
        Long detailId = request.getDetailId();
        String spuNo = request.getSpuNo();

        ContainerEntity containerEntity = containerDao.selectOne(new QueryWrapper<ContainerEntity>().eq("cont_no", contNo));
        if (ObjectUtils.isEmpty(containerEntity) || StatusEnum.USABLE.getCode() != containerEntity.getStatus()) {
            throw new RootException("托盘[" + contNo + "]未维护基础信息!不可使用!");
        }
        List<StockEntity> stockEntityList = stockDao.selectList(new QueryWrapper<StockEntity>().eq("cont_no", contNo).eq("position_type", "HW"));
        if (!CollectionUtils.isEmpty(stockEntityList) && LocateTypeEnum.HW.getCode().equals(stockEntityList.get(0).getPositionType())) {
            throw new RootException("托盘[" + contNo + "]已在库内，不可用!");
        }
//        根据托盘类型与站台类型匹配入库站台
        StationEntity stationEntity = stationDao.selectOne(new QueryWrapper<StationEntity>().eq("dvc_no", containerEntity.getContType())
                .eq("station_type", "IN"));
        if (ObjectUtils.isEmpty(stationEntity) || StatusEnum.USABLE.getCode() != stationEntity.getStationSt()) {
            throw new RootException("区域[" + containerEntity.getContType() + "]的入库站台未维护！");
        }
        String dvcNo = stationEntity.getDvcNo();
        String stationNo = stationEntity.getStationNo();
        PurchaseDetailEntity purchaseDetailEntity = purchaseDetailDao.selectById(detailId);
        if (ObjectUtils.isEmpty(purchaseDetailEntity)
                || PurchaseStatusEnum.COMPLETE.getCode() == purchaseDetailEntity.getDetailSt()) {
            throw new RootException("入库单不存在或当前状态不可收货!");
        }
        PurchaseEntity purchaseEntity = purchaseDao.selectById(purchaseDetailEntity.getPurchaseId());
        if (ObjectUtils.isEmpty(purchaseEntity)) {
            throw new RootException("入库主单不存在!");
        }
        BigDecimal needQty = purchaseDetailEntity.getPlanQty().subtract(purchaseDetailEntity.getBindQty());
        if (needQty.compareTo(num) < 0) {
            throw new RootException("收货量大于计划量！");
        }
        SpuEntity spuEntity = spuDao.selectOne(new QueryWrapper<SpuEntity>().eq("spu_no", spuNo));
        if (ObjectUtils.isEmpty(spuEntity)) {
            throw new RootException("物料不存在!");
        }

        Date startDate = new Date();
        String code = "0";
        String msg;
        String comment = "入库登记";
        String work = "PurchaseApiController.purchaseRegister";
        Long userId = Constant.ClientCode.PDA.getId();
        //日志记录
        LogWorkEntity insertLogWork = new LogWorkEntity();
        insertLogWork.setWorkName(comment);
        insertLogWork.setRelateNo(purchaseDetailEntity.getDetailId().toString());
        insertLogWork.setWorkProc(work);
        insertLogWork.setCreator(userId);
        insertLogWork.setCreateDate(startDate);
        //生成库存
        StockEntity insertStock = new StockEntity();
        insertStock.setContNo(contNo);
        insertStock.setCurPosition(stationNo);
        insertStock.setPositionType(LocateTypeEnum.ST.getCode());
        insertStock.setSpuNo(spuNo);
        insertStock.setSpuName(spuEntity.getSpuName());
        insertStock.setStockSt(StatusEnum.DISABLE.getCode());
        insertStock.setLockSt(StatusEnum.USABLE.getCode());
        insertStock.setStockQty(num);
        insertStock.setLockedQty(BigDecimal.ZERO);
        insertStock.setCreator(userId);
        insertStock.setCreateDate(startDate);
        insertStock.setUpdater(userId);
        insertStock.setUpdateDate(startDate);
        insertStock.setComments(comment);
        try {
            stockDao.insert(insertStock);
        } catch (Exception e) {
            logger.info("生成库存失败", e);
            code = "1001";
            msg = e.getMessage();
            insertLogWork.setWorkCode(code);
            insertLogWork.setWorkMsg(msg);
            logWorkService.logWorkInsert(insertLogWork);
            throw new RootException("生成库存失败！");
        }
        //货位预分配（需要根据具体的区域划分）
        LocateEntity locateEntity = null;
        if (ContTypeEnum.HK.getCode().equals(dvcNo)) {
            try {
                locateEntity = locateService.locatePreAllocation(containerEntity, contNo, dvcNo, startDate, userId, comment);
            } catch (Exception e) {
                logger.info("货位预占用失败", e);
                code = "1001";
                msg = e.getMessage();
                insertLogWork.setWorkCode(code);
                insertLogWork.setWorkMsg(msg);
                logWorkService.logWorkInsert(insertLogWork);
                throw new RootException("货位预占用失败！");
            }
        }
        //生成task任务
        TaskEntity insertTask = new TaskEntity();
        insertTask.setTaskType(TaskTypeEnum.WAREHOUSE_ENTRY.getCode());
        insertTask.setTaskSubType(TaskSubTypeEnum.WHOLE_PALLET_STORAGE.getCode());
        insertTask.setContNo(contNo);
        insertTask.setSpuNo(spuNo);
        insertTask.setSpuName(spuEntity.getSpuName());
        insertTask.setPlanQty(num);
        insertTask.setActlQty(num);
        insertTask.setFromPosition(stationNo);
        insertTask.setCurrentPosition(stationNo);
        if (!ObjectUtils.isEmpty(locateEntity)) {
            insertTask.setToPosition(locateEntity.getLocateNo());
        }
        insertTask.setDetailId(purchaseDetailEntity.getDetailId());
        insertTask.setMtId(purchaseDetailEntity.getPurchaseId());
        insertTask.setMtNo(purchaseEntity.getPurchaseNo());
        insertTask.setStockId(insertStock.getStockId());
        insertTask.setTaskSt(TaskStEnum.INITIAL.getCode());
        insertTask.setCreator(userId);
        insertTask.setUpdater(userId);
        insertTask.setCreateDate(startDate);
        insertTask.setComments(comment);
        try {
            taskService.taskInsert(insertTask);
        } catch (RootException e) {
            logger.info("保存入库任务失败", e);
            code = "1001";
            msg = e.getMessage();
            insertLogWork.setWorkCode(code);
            insertLogWork.setWorkMsg(msg);
            logWorkService.logWorkInsert(insertLogWork);
            throw new RootException(msg);
        } catch (Exception e) {
            logger.info("保存入库任务失败", e);
            code = "1001";
            msg = e.getMessage();
            insertLogWork.setWorkCode(code);
            insertLogWork.setWorkMsg(msg);
            logWorkService.logWorkInsert(insertLogWork);
            throw new RootException("保存入库任务失败");
        }
        //修改入库明细
        PurchaseDetailEntity updatePurchase = new PurchaseDetailEntity();
        updatePurchase.setDetailId(purchaseDetailEntity.getDetailId());
        updatePurchase.setBindQty(purchaseDetailEntity.getBindQty().add(num));
        if (updatePurchase.getBindQty().compareTo(purchaseDetailEntity.getPlanQty()) == 0) {
            updatePurchase.setDetailSt(PurchaseStatusEnum.COMPLETE.getCode());
        } else {
            updatePurchase.setDetailSt(PurchaseStatusEnum.RECEIVING.getCode());
        }
        updatePurchase.setUpdater(userId);
        updatePurchase.setUpdateDate(startDate);
        updatePurchase.setComments(comment);
        try {
            purchaseDetailDao.updateById(updatePurchase);
        } catch (Exception e) {
            logger.info("修改入库明细失败", e);
            code = "1001";
            msg = e.getMessage();
            insertLogWork.setWorkCode(code);
            insertLogWork.setWorkMsg(msg);
            logWorkService.logWorkInsert(insertLogWork);
            throw new RootException("修改入库明细失败！");
        }
        //修改主单状态
        Map<String, Object> map = new HashMap<>();
        map.put("comment", comment);
        map.put("purchaseId", purchaseDetailEntity.getPurchaseId());
        map.put("userId", userId);
        try {
            commonService.purchaseStatus(map);
        } catch (Exception e) {
            logger.info("修改入库主单失败", e);
            code = "1001";
            msg = e.getMessage();
            insertLogWork.setWorkCode(code);
            insertLogWork.setWorkMsg(msg);
            logWorkService.logWorkInsert(insertLogWork);
            throw new RootException("修改入库主单失败！");
        }
        msg = "物料[" + spuEntity.getSpuName() + "],数量[" + num + "]收货完成,容器条码[" + contNo + "]";
        insertLogWork.setWorkCode(code);
        insertLogWork.setWorkMsg(msg);
        logWorkService.logWorkInsert(insertLogWork);
        return new Result().ok(msg);
    }

    @Override
    public void store(PurchaseDetailRequest request) {
        //日志记录
        LogWorkEntity insertLogWork = new LogWorkEntity();

        String contNo = request.getContNo();
        ContainerEntity containerEntity = containerDao.selectOne(new QueryWrapper<ContainerEntity>().eq("cont_no", contNo));
        if (ObjectUtils.isEmpty(containerEntity) || StatusEnum.USABLE.getCode() != containerEntity.getStatus()) {
            throw new RootException("托盘不存在或者不可用!");
        }
        List<StockEntity> stockEntityList = stockDao.selectList(new QueryWrapper<StockEntity>().eq("cont_no", contNo));
        if (!CollectionUtils.isEmpty(stockEntityList) && LocateTypeEnum.HW.getCode().equals(stockEntityList.get(0).getPositionType())) {
            throw new RootException("托盘在库，不可用!");
        }
        List<TaskEntity> taskEntityList = taskDao.selectList(new QueryWrapper<TaskEntity>()
                .eq("task_type", TaskTypeEnum.WAREHOUSE_ENTRY.getCode())
                .eq("cont_no", contNo)
                .eq("task_st", TaskStEnum.INITIAL.getCode()));
        if (CollectionUtils.isEmpty(taskEntityList)) {
            throw new RootException("此托盘不存在任务！");
        }
        //入库放行时需校验下终点库位状态是否为空闲，如果不是，就再重新分配货位
        LocateEntity byLocateNo = locateDao.selectOne(new QueryWrapper<LocateEntity>()
                .eq("locate_no", taskEntityList.get(0).getToPosition()));
        LocateEntity locateEntitys = null;
        if (ObjectUtils.isNotEmpty(byLocateNo)) {
            if (byLocateNo.getLocateSt() != 0) {
                String dvcNo = ContTypeEnum.HK.getCode();
                Long userId = Constant.ClientCode.WCS.getId();
                Date startDate = new Date();
                String comment = "WCS通知-" + contNo;
                if (ContTypeEnum.HK.getCode().equals(dvcNo)) {
                    try {
                        locateEntitys = locateService.locatePreAllocation(containerEntity, contNo, dvcNo, startDate, userId, comment);
                        //将当前托盘的所有任务的终点都改掉
                        for (TaskEntity taskEntity : taskEntityList) {
                            taskEntity.setToPosition(locateEntitys.getLocateNo());
                            taskEntity.setUpdateDate(new Date());
                            taskEntity.setComments("二次分配入库库位");
                            taskDao.updateById(taskEntity);
                        }
                    } catch (Exception e) {
                        logger.info("货位分配失败", e);
                        insertLogWork.setWorkCode("1001");
                        insertLogWork.setWorkMsg(e.getMessage());
                        logWorkService.logWorkInsert(insertLogWork);
                        throw new RootException("货位分配失败！");
                    }
                }
            }
        }

        RmsNoticeEntity rmsNoticeEntity = rmsNoticeDao.selectOne(new QueryWrapper<RmsNoticeEntity>().eq("cont_no", contNo));
        if (ObjectUtils.isEmpty(rmsNoticeEntity) && ContTypeEnum.YF.getCode().equals(containerEntity.getContType())) {
            throw new RootException("请先完成高度检测！");
        }
        TaskEntity taskEntity = taskEntityList.get(0);
        Date startDate = new Date();
        String code = "0";
        String msg = "入库成功";
        String comment = "入库放行";
        String work = "PurchaseApiController.store";
        Long userId = Constant.ClientCode.PDA.getId();

        insertLogWork.setWorkName(comment);
//        insertLogWork.setRelateNo(purchaseId.toString());
        insertLogWork.setWorkProc(work);
        insertLogWork.setCreator(userId);
        insertLogWork.setCreateDate(startDate);
        String toPosition = taskEntity.getToPosition();
        //YF货位预占用
        if (ContTypeEnum.YF.getCode().equals(containerEntity.getContType())) {
            LocateEntity locateEntity;
            try {
                locateEntity = locateService.locatePreAllocation(containerEntity, contNo, ContTypeEnum.YF.getCode(), startDate, userId, comment);
                toPosition = locateEntity.getLocateNo();
            } catch (Exception e) {
                logger.info("货位预占用失败", e);
                code = "1001";
                msg = e.getMessage();
                insertLogWork.setWorkCode(code);
                insertLogWork.setWorkMsg(msg);
                logWorkService.logWorkInsert(insertLogWork);
                throw new RootException("货位预占用失败！");
            }
            try {
                taskDao.update(null, new UpdateWrapper<TaskEntity>()
                        .eq("cont_no", contNo)
                        .eq("task_type", TaskTypeEnum.WAREHOUSE_ENTRY.getCode())
                        .eq("task_st", TaskStEnum.INITIAL.getCode())
                        .set("to_position", toPosition));
            } catch (Exception e) {
                logger.info("任务修改入库货位失败", e);
                code = "1001";
                msg = e.getMessage();
                insertLogWork.setWorkCode(code);
                insertLogWork.setWorkMsg(msg);
                logWorkService.logWorkInsert(insertLogWork);
                throw new RootException("任务修改入库货位失败！");
            }
        }
        //生成搬送任务
        PostWcsEntity insertPostWcs = new PostWcsEntity();
        insertPostWcs.setContNo(contNo);
        insertPostWcs.setContType(containerEntity.getContType());
        insertPostWcs.setTrkType(PostWcsTypeEnum.IN.getCode());
        insertPostWcs.setFrmPos(taskEntity.getFromPosition());
        insertPostWcs.setCurPos(taskEntity.getCurrentPosition());
        insertPostWcs.setToPos(toPosition);
        insertPostWcs.setTrkSt(PostWcsStEnum.INITIAL.getCode());
        insertPostWcs.setIsFull(PostWcsFullEnum.FULL.getCode());
        insertPostWcs.setCreator(userId);
        insertPostWcs.setCreateDate(startDate);
        insertPostWcs.setUpdater(userId);
        insertPostWcs.setUpdateDate(startDate);
        insertPostWcs.setComments(comment);
        try {
            postWcsService.postWcsInsert(insertPostWcs);
        } catch (Exception e) {
            logger.info("生成搬送任务失败", e);
            code = "1001";
            msg = e.getMessage();
            insertLogWork.setWorkCode(code);
            insertLogWork.setWorkMsg(msg);
            logWorkService.logWorkInsert(insertLogWork);
            throw new RootException("生成搬送任务失败！");
        }
        //日志记录
        insertLogWork.setWorkCode(code);
        insertLogWork.setWorkMsg(msg);
        logWorkService.logWorkInsert(insertLogWork);
    }

    @Override
    public QueryWrapper<PurchaseDetailEntity> getWrapper(Map<String, Object> params) {
        final String purchaseId = (String) params.get("purchaseId");
        final String detailId = (String) params.get("detailId");
        final String spuNo = (String) params.get("spuNo");
        final String spuName = (String) params.get("spuName");
        final String deliverNo = (String) params.get("deliverNo");
        final String detailSt = (String) params.get("detailSt");

        QueryWrapper<PurchaseDetailEntity> wrapper = new QueryWrapper<>();
        wrapper.eq(StringUtils.isNotBlank(purchaseId), "purchase_id", purchaseId);
        wrapper.eq(StringUtils.isNotBlank(detailId), "detail_id", detailId);
        wrapper.eq(StringUtils.isNotBlank(detailSt), "detail_st", detailSt);
        wrapper.like(StringUtils.isNotBlank(spuNo), "spu_no", spuNo);
        wrapper.like(StringUtils.isNotBlank(spuName), "spu_name", spuName);
        wrapper.like(StringUtils.isNotBlank(deliverNo), "deliver_no", deliverNo);

        return wrapper;
    }

}